/**
 * Copyright 2016 Yahoo Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.yahoo.pulsar.common.naming;

import static com.google.common.base.Preconditions.checkNotNull;

import java.net.URI;
import java.net.URISyntaxException;

import com.google.common.base.Objects;

public class NamespaceName implements ServiceUnitId {

    private final String namespace;

    private String property;
    private String cluster;
    private String localName;

    public NamespaceName(String namespace) {
        try {
            checkNotNull(namespace);
        } catch (NullPointerException e) {
            throw new IllegalArgumentException("Invalid null namespace: " + namespace);
        }

        // Verify it's a proper namespace
        validateNamespaceName(namespace);
        this.namespace = namespace;
    }

    public NamespaceName(String property, String cluster, String namespace) {
        validateNamespaceName(property, cluster, namespace);
        this.namespace = property + '/' + cluster + '/' + namespace;
        this.property = property;
        this.cluster = cluster;
        this.localName = namespace;
    }

    public String getProperty() {
        return property;
    }

    public String getCluster() {
        return cluster;
    }

    public String getLocalName() {
        return localName;
    }

    public boolean isGlobal() {
        return "global".equals(cluster);
    }

    public String getPersistentTopicName(String localTopic) {
        return getDestinationName(DestinationDomain.persistent, localTopic);
    }

    /**
     * Compose the destination name from namespace + destination
     *
     * @param domain
     * @param destination
     * @return
     */
    String getDestinationName(DestinationDomain domain, String destination) {
        try {
            checkNotNull(domain);
            NamedEntity.checkName(destination);
            return String.format("%s://%s/%s", domain.toString(), namespace, destination);
        } catch (NullPointerException e) {
            throw new IllegalArgumentException("Null pointer is invalid as domain for destination.", e);
        }
    }

    @Override
    public String toString() {
        return namespace;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof NamespaceName) {
            NamespaceName other = (NamespaceName) obj;
            return Objects.equal(namespace, other.namespace);
        }

        return false;
    }

    @Override
    public int hashCode() {
        return namespace.hashCode();
    }

    public static void validateNamespaceName(String property, String cluster, String namespace) {
        try {
            checkNotNull(property);
            checkNotNull(cluster);
            checkNotNull(namespace);
            if (property.isEmpty() || cluster.isEmpty() || namespace.isEmpty()) {
                throw new IllegalArgumentException(
                        String.format("Invalid namespace format. namespace: %s/%s/%s", property, cluster, namespace));
            }
            NamedEntity.checkName(property);
            NamedEntity.checkName(cluster);
            NamedEntity.checkName(namespace);
        } catch (NullPointerException e) {
            throw new IllegalArgumentException(
                    String.format("Invalid namespace format. namespace: %s/%s/%s", property, cluster, namespace), e);
        }
    }

    private void validateNamespaceName(String namespace) {
        // assume the namespace is in the form of <property>/<cluster>/<namespace>
        try {
            checkNotNull(namespace);
            String testUrl = String.format("http://%s", namespace);
            URI uri = new URI(testUrl);
            checkNotNull(uri.getPath());
            NamedEntity.checkURI(uri, testUrl);

            String[] parts = uri.getPath().split("/");
            if (parts.length != 3) {
                throw new IllegalArgumentException("Invalid namespace format. namespace: " + namespace);
            }
            validateNamespaceName(uri.getHost(), parts[1], parts[2]);

            property = uri.getHost();
            cluster = parts[1];
            localName = parts[2];
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException("Invalid namespace format. namespace: " + namespace, e);
        } catch (NullPointerException e) {
            throw new IllegalArgumentException("Invalid namespace format. namespace: " + namespace, e);
        }
    }

    @Override
    public NamespaceName getNamespaceObject() {
        return this;
    }

    @Override
    public boolean includes(DestinationName dn) {
        return this.equals(dn.getNamespaceObject());
    }
}
